#include "../TcpSocket.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

// TODO: move this to general part
#include <string.h>

namespace Greatewall
{
    namespace Network
    {
        SysSockHandler
        TcpSocket::createTcpSock()
        {
            return socket(AF_INET, SOCK_STREAM, 0);
        }

        bool
        TcpSocket::send(const char* buf, ssize_t& size, bool non_block)
        {
            if (!_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_mode == SERVER)
            {
                // A server socket is passive and should never send anything
                // TODO: Error handling
                return false;
            }

            return IpSocket::send(buf, size, non_block);
        }

        bool
        TcpSocket::recv(char*& buf, ssize_t& size, bool non_block)
        {
            if (!_inited)
            {
                // TODO: Error handling
                return false;
            }

            if (_mode == SERVER)
            {
                // A server socket is only used for accepting connection
                // Should never read any real data
                // TODO: Error handling
                return false;
            }

            return IpSocket::recv(buf, size, non_block);
        }

        bool
        TcpSocket::initTcpSock(std::string host, short port, std::string netif)
        {

            if (_inited)
            {
                // Cannot initialize twice
                // TODO: Error handling
                return false;
            }

            // Check if we should create a new sock
            if (_hdl < 0 && ((_hdl = createTcpSock()) < 0))
            {
                // TODO: Error handling
                return false;
            }

            if (host.empty() || port <= 0)
            {
                // TODO: Error handling
                return false;
            }

            in_addr_t srv_addr = -1UL;

            if (!inet_aton(host.c_str(), (in_addr*)&srv_addr))
            {
                // TODO: Error handling
                return false;
            }

            sockaddr_in srv;
            memset(&srv, 0, sizeof(srv));
            srv.sin_family      = AF_INET;
            srv.sin_addr.s_addr = srv_addr;
            srv.sin_port        = htons(port);

            if (connect(_hdl, (sockaddr*)&srv, sizeof(srv)) < 0)
            {
                // TODO: Error handling
                return false;
            }      

            _mode   = CLIENT;
            _host   = host;
            _port   = port;
            _netif  = netif;
            _inited = true;

            return true;
        }

        bool
        TcpSocket::initTcpSock(short port, int backlog, std::string& netif)
        {
            if (_inited)
            {
                // Cannot initialize twice
                // TODO: Error handling
                return false;
            }

            if (_hdl < 0 && ((_hdl = createTcpSock()) < 0))
            {
                // TODO: Error handling
                return false;
            }

            if (port < 0)
            {
                // TODO: Error handling
                return false;
            }

            sockaddr_in srv;
            memset(&srv, 0, sizeof(srv));
            srv.sin_family      = AF_INET;
            srv.sin_addr.s_addr = htonl(INADDR_ANY);
            srv.sin_port        = htons(port);

            int flag_on = 1;

            if (setsockopt(_hdl, SOL_SOCKET, SO_REUSEADDR, &flag_on, sizeof(int)) == -1)
            {
                // TODO: Error handling
                return false;
            }

            if (bind(_hdl, (sockaddr*)&srv, sizeof(srv)) == -1)
            {
                // TODO: Error handling
                return false;
            }

            if (listen(_hdl, backlog) == -1)
            {
                return false;
            }

            _mode  = SERVER;
            _port  = port;
            _netif = netif;
            _inited= true;

            return true;
        }

        bool
        TcpSocket::initTcpSock(SysSockHandler hdl, std::string host, int port)
        {
            if (_inited)
            {
                // Cannot initialize twice
                return false;
            }

            _mode   = CLIENT_HANDLER;
            _hdl    = hdl;
            _host   = host;
            _port   = port;
            _inited = true;

            return true;
        }

        void
        TcpSocket::close()
        {
            if (_hdl > 0)
            {
                ::close(_hdl);
                _inited = false;
            }
        }
    }
}

